#![allow(dead_code)]

use anyhow::anyhow;
use chrono::{Duration, Local};
use futures::TryFutureExt;
use futures::future::try_join_all;
use polars::frame::DataFrame;
use reqwest::{Client, RequestBuilder, Response};
use serde::{Deserialize, Serialize};
use sq_global::{DATE_FORMAT, TU_SHARE_HOST, TU_SHARE_TOKEN_CFG, get_tushare_idx};
use std::collections::HashMap;
use std::sync::OnceLock;

#[derive(Debug)]
struct TuShareClient {
    client: Client,
    host: &'static str,
}

impl TuShareClient {
    fn new() -> Self {
        Self {
            client: sq_global::http_cli(),
            host: TU_SHARE_HOST,
        }
    }
    pub fn post(&self) -> RequestBuilder {
        self.client.post(self.host)
    }
}

static TUSHARE_CLI: OnceLock<TuShareClient> = OnceLock::new();
static TUSHARE_TOKENS: OnceLock<Vec<String>> = OnceLock::new();

pub fn init() {
    TUSHARE_CLI
        .set(TuShareClient::new())
        .expect("tushare client init failed !!");
    TUSHARE_TOKENS
        .set(sq_config::get_unchecked::<Vec<String>>(TU_SHARE_TOKEN_CFG))
        .expect("tushare tokens init failed !!");
}

#[inline]
fn cli() -> &'static TuShareClient {
    TUSHARE_CLI.get().expect("tu share client not initialized")
}

#[inline]
async fn token() -> Option<&'static str> {
    let guard = get_tushare_idx().read().await;
    let bias = *guard;

    let token = TUSHARE_TOKENS
        .get()
        .and_then(|tokens| tokens.get(bias % tokens.len()))
        .map(|token| token.as_str());

    if let Some(token) = token {
        tracing::info!("use token {token}...");
    }

    drop(guard);
    token
}

#[derive(Debug, Serialize)]
struct TuShareReq {
    api_name: &'static str,
    token: &'static str,
    params: HashMap<&'static str, String>,
}

impl TuShareReq {
    fn builder() -> TuShareReqBuilder {
        TuShareReqBuilder::default()
    }
}

#[derive(Default)]
struct TuShareReqBuilder {
    api_name: Option<&'static str>,
    params: HashMap<&'static str, String>,
}

impl TuShareReqBuilder {
    fn api_name(mut self, api_name: &'static str) -> Self {
        self.api_name = Some(api_name);
        self
    }
    fn param(mut self, key: &'static str, val: String) -> Self {
        self.params.insert(key, val);
        self
    }
    fn params(mut self, params: impl IntoIterator<Item = (&'static str, String)>) -> Self {
        self.params.extend(params);
        self
    }
    async fn build(self) -> anyhow::Result<TuShareReq> {
        Ok(TuShareReq {
            api_name: self.api_name.ok_or(anyhow!("missing api name"))?,
            token: token().await.ok_or(anyhow!("missing token"))?,
            params: self.params,
        })
    }
}

#[derive(Deserialize)]
struct Data {
    fields: Vec<String>,
    items: Vec<Vec<serde_json::Value>>, // 动态类型适配
    has_more: bool,
    count: isize,
}

impl Data {
    pub fn is_empty(&self) -> bool {
        self.items.is_empty() || self.fields.is_empty()
    }
}

#[derive(Deserialize)]
struct TuShareResp {
    code: i32,
    msg: String,
    request_id: String,
    data: Option<Data>,
}

pub async fn recent_dates(api: &'static str, days: i64) -> anyhow::Result<Vec<DataFrame>> {
    let now = Local::now();
    let vec: Vec<_> = (0..days)
        .into_iter()
        .map(|v| {
            let time = now - Duration::days(v);
            let date = time.format(DATE_FORMAT).to_string();
            let param = vec![("trade_date", date)];
            load_data(api, param)
        })
        .collect();
    try_join_all(vec).await
}

pub async fn load_data(
    api: &'static str,
    params: Vec<(&'static str, String)>,
) -> anyhow::Result<DataFrame> {
    let param_json = serde_json::to_string(&params)?;
    let req = TuShareReq::builder()
        .api_name(api)
        .params(params)
        .build()
        .await?;

    let resp = cli()
        .post()
        .json(&req)
        .send()
        .and_then(Response::json::<TuShareResp>)
        .await?;

    // 优化后的代码
    let table = match (resp.code, resp.data) {
        (0, Some(data)) if !data.is_empty() => data,
        (0, Some(_)) => {
            tracing::warn!("empty data with api {api} by params：{param_json}");
            return Ok(DataFrame::empty());
        }
        (40203, _) => {
            let mut guard = get_tushare_idx().write().await;
            *guard += 1;
            drop(guard);
            return Err(anyhow!(
                "err request api {api} : {param_json}, message : {}",
                resp.msg
            ));
        }
        (_, _) => {
            return Err(anyhow!(
                "err request api {api} : {param_json}, message : {}",
                resp.msg
            ));
        }
    };

    let mut buffer = Vec::with_capacity(table.items.len() * 128);
    // 转换数据为 NDJSON
    buffer = sq_polars::convert_to_ndjson(buffer, table.fields, table.items)?;

    Ok(sq_polars::build_pl_df(buffer)?)
}
